package tr.helper;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.Method;
import com.baidu.aip.ocr.AipOcr;
import io.vavr.Tuple;
import io.vavr.Tuple2;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.net.HttpCookie;
import java.net.InetAddress;
import java.net.URL;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonBar;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Control;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.image.ImageView;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.util.Callback;
import net.sourceforge.tess4j.ITesseract;
import net.sourceforge.tess4j.Tesseract;
import net.sourceforge.tess4j.TesseractException;
import org.apache.commons.lang3.StringUtils;
import org.json.JSONArray;
import org.json.JSONObject;
import org.simplejavamail.email.Email;
import org.simplejavamail.email.EmailBuilder;
import org.simplejavamail.mailer.Mailer;
import tr.helper.controller.log.LogController;
import tr.helper.repository.BaseRepository;
import tr.helper.view.annotation.Alias;
import tr.helper.view.annotation.View;

/**
 * tr-service
 *
 * @author MrYe
 * @date 2022-01-11 13:39
 */
public class CommonUtil {

    public static int mode = 1;

    private static Snowflake snowflake = IdUtil.createSnowflake(1L, 1L);

    //public static final AipOcr client = new AipOcr("17878489", "z81GCf24hTNvB0N25kMBQEVz", "Tg6RAcBSRy9lBcxws0OONufZVqqjLGsy");
    public static final AipOcr client = new AipOcr("25508623", "BTijIRyuHB2h1GCKwuMj9ZGK", "i6DRPhXAq8ho2VybyPLGX2zOaDS6DTtr");

    public static String getOsName() {
        String os = System.getProperty("os.name");
        //Windows操作系统
        if (os != null && os.toLowerCase().startsWith("windows")) {
            return "windows";
        } else if (os != null && os.toLowerCase().startsWith("linux")) {
            //Linux操作系统
            return "linux";
        } else { //其它操作系统
            return os;
        }
    }

    public static void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static List<HttpCookie> stringToCookie(String cookieStr) {
        List<HttpCookie> cookies = new ArrayList<>();
        for (String cookie : StringUtils.split(cookieStr, ";")) {
            String[] cks = StringUtils.split(cookie, "=");
            if (cks.length > 0) {
                HttpCookie ck = new HttpCookie(cks[0], cks[1]);
                cookies.add(ck);
            }
        }
        return cookies;
    }

    public static String cookieToString(List<HttpCookie> cookies) {
        StringBuilder cookieBuilder = new StringBuilder();
        for (HttpCookie cookie : cookies) {
            cookieBuilder.append(cookie.toString()).append(";");
        }
        return cookieBuilder.toString();
    }

    public static String getHomeImagePath() {
        return FileUtil.getUserHomePath() + "/tr-helper-images/";
    }

    public static URL getFxmlResource(String fxmlPath) {
        if (fxmlPath.startsWith("/")) {
            fxmlPath = fxmlPath.substring(1, fxmlPath.length() - 1);
        }
        return Thread.currentThread().getContextClassLoader().getResource("tr/helper/controller/" + fxmlPath);
    }

    public static void log(String msg, Object... args) {
        System.out.println(String.format(msg, args));
    }

    public static void log(Object msg, Object... args) {
        System.out.println(String.format(msg.toString(), args));
    }

    public static <S> TableColumn[] getTableCols(Class<S> clazz) {
        return getTableCols(clazz, null);
    }

    public static <S> TableColumn[] getTableCols(Class<S> clazz, Consumer<List<TableColumn>> consumer) {
        if (!clazz.isAnnotationPresent(View.class)) {
            throw new RuntimeException("Class:" + clazz + " must modification @View");
        }
        List<TableColumn> columns = new ArrayList<>();

        final Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            if (Modifier.isStatic(field.getModifiers())) {
                continue;
            }
            int width = 0;
            String type = field.getGenericType().toString();
            String text = field.getName();
            final Alias alias = field.getAnnotation(Alias.class);
            if (null != alias) {
                if (alias.ignore()) {
                    continue;
                }
                text = alias.value();
                width = alias.width();

                if (alias.gif()) {

                    TableColumn<S, ImageView> gifColumn = new TableColumn<>(text);
                    if (width > 0) {
                        gifColumn.setPrefWidth(width);
                    }
                    gifColumn.setCellValueFactory(new PropertyValueFactory<>(field.getName()));

                    gifColumn.setCellFactory(new Callback<TableColumn<S, ImageView>, TableCell<S, ImageView>>() {
                        @Override
                        public TableCell<S, ImageView> call(TableColumn<S, ImageView> param) {
                            return new TableCell<S, ImageView>() {
                                @Override
                                protected void updateItem(ImageView item, boolean empty) {
                                    super.updateItem(item, empty);
                                    if (item != null && !empty) {
                                        setGraphic(item);
                                    } else {
                                        setGraphic(null);
                                    }
                                }
                            };
                        }
                    });

                    columns.add(gifColumn);
                }
            }

            if ("class java.lang.String".equals(type)) {
                TableColumn<S, String> column = new TableColumn<>(text);
                column.setCellValueFactory(new PropertyValueFactory<>(field.getName()));
                if (width > 0) {
                    column.setPrefWidth(width);
                }
                if (null != alias && alias.br()) {
                    column.setCellFactory(tc -> {
                        TableCell<S, String> cell = new TableCell<>();
                        Text txt = new Text();
                        if (alias.fontSize() > 0) {
                            txt.setFont(Font.font(alias.fontSize()));
                        }
                        cell.setGraphic(txt);
                        if (alias.br()) {
                            cell.setPrefHeight(Control.USE_COMPUTED_SIZE);
                            txt.wrappingWidthProperty().bind(column.widthProperty());
                        }
                        txt.textProperty().bind(cell.itemProperty());
                        return cell;
                    });
                }
                columns.add(column);
            } else if ("class java.lang.Long".equals(type)) {
                TableColumn<S, Long> column = new TableColumn<>(text);
                column.setCellValueFactory(new PropertyValueFactory<>(field.getName()));
                if (width > 0) {
                    column.setPrefWidth(width);
                }
                columns.add(column);
            } else if ("class java.lang.Integer".equals(type)) {
                TableColumn<S, Integer> column = new TableColumn<>(text);
                column.setCellValueFactory(new PropertyValueFactory<>(field.getName()));
                if (width > 0) {
                    column.setPrefWidth(width);
                }
                columns.add(column);
            } else if ("class java.lang.Double".equals(type)) {
                TableColumn<S, Double> column = new TableColumn<>(text);
                column.setCellValueFactory(new PropertyValueFactory<>(field.getName()));
                if (width > 0) {
                    column.setPrefWidth(width);
                }
                columns.add(column);
            } else if ("class java.lang.Date".equals(type)) {
                TableColumn<S, Date> column = new TableColumn<>(text);
                column.setCellValueFactory(new PropertyValueFactory<>(field.getName()));
                if (width > 0) {
                    column.setPrefWidth(width);
                }
                columns.add(column);
            } else if ("class java.lang.Boolean".equals(type)) {
                TableColumn<S, Boolean> column = new TableColumn<>(text);
                column.setCellValueFactory(new PropertyValueFactory<>(field.getName()));
                if (width > 0) {
                    column.setPrefWidth(width);
                }
                columns.add(column);
            } else if ("class java.math.BigDecimal".equals(type)) {
                TableColumn<S, BigDecimal> column = new TableColumn<>(text);
                column.setCellValueFactory(new PropertyValueFactory<>(field.getName()));
                if (width > 0) {
                    column.setPrefWidth(width);
                }
                columns.add(column);
            }
        }

        if (null != consumer) {
            consumer.accept(columns);
        }

        TableColumn[] columnArray = new TableColumn[columns.size()];
        int i = 0;
        for (TableColumn column : columns) {
            columnArray[i] = column;
            i++;
        }
        return columnArray;
    }


    /**
     * 弹出一个通用的确定对话框
     *
     * @param header 对话框的信息标题
     * @param msg    对话框的信息
     * @return 用户点击了是或否
     */
    public static boolean confirm(Stage stage, String header, String msg) {
        Alert alert = new Alert(Alert.AlertType.CONFIRMATION, msg, new ButtonType("取消", ButtonBar.ButtonData.NO),
            new ButtonType("确定", ButtonBar.ButtonData.YES));
        // 设置窗口的标题
        alert.setTitle("确认");
        alert.setHeaderText(header);
        // 设置对话框的 icon 图标，参数是主窗口的 stage
        if (null != stage) {
            alert.initOwner(stage);
        }
        // showAndWait() 将在对话框消失以前不会执行之后的代码
        Optional<ButtonType> buttonType = alert.showAndWait();
        // 根据点击结果返回
        if (buttonType.isPresent() && buttonType.get().getButtonData().equals(ButtonBar.ButtonData.YES)) {
            return true;
        } else {
            return false;
        }
    }

    public static void alert(Stage stage, String header, String msg) {
        Alert alert = new Alert(Alert.AlertType.INFORMATION);
        alert.setTitle("消息");
        alert.setHeaderText(header);
        alert.setContentText(msg);
        if (null != stage) {
            alert.initOwner(stage);
        }
        alert.showAndWait();
    }

    public static void alert(Stage stage, String msg) {
        alert(stage, "", msg);
    }

    public static Long diffMinutes(LocalDateTime start, LocalDateTime end) {
        final Duration duration = Duration.between(start, end);
        return duration.toMinutes();
    }

    public static LocalDateTime asLocalDateTime(Date date) {
        return Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDateTime();
    }

    public static String getNumber(String str) {
        String regEx = "[^0-9]";
        Pattern p = Pattern.compile(regEx);
        Matcher m = p.matcher(str);
        return StringUtils.trim(m.replaceAll(""));
    }

    public static Integer getWeek(String text) throws ParseException {
        String date = subDate(text);
        Calendar calendar = Calendar.getInstance();
        calendar.setFirstDayOfWeek(Calendar.SUNDAY);
        calendar.setTime(new SimpleDateFormat("yyyy-MM-dd").parse(date));
        return calendar.get(Calendar.WEEK_OF_YEAR);
    }

    public static Integer getMonth(String text) {
        String date = StringUtils.replace(text, "Open : ", "");
        return DateUtil.month(DateUtil.parse(date)) + 1;
    }

    private static String subDate(String text) {
        return StringUtils.substring(text, 0, text.indexOf("<"));
    }

    public static String asString(Object obj) {
        return obj == null ? StringUtils.EMPTY : obj.toString();
    }

    public static int asInt(Object obj) {
        return obj == null ? 0 : Double.valueOf(obj.toString()).intValue();
    }

    public static BigDecimal convertDecimal(Object obj) {
        return obj == null ? BigDecimal.ZERO : BigDecimal.valueOf(Double.parseDouble(obj.toString()));
    }

    public static BigDecimal getBalance(String text) {
        String b = StringUtils.replace(text, "$", StringUtils.EMPTY).trim();
        return BigDecimal.valueOf(Double.parseDouble(b.replace(",", StringUtils.EMPTY)));
    }

    public static Map<String, String> cookieToMap(String value) {
        Map<String, String> map = new HashMap<>();
        value = value.replace(" ", "");
        if (value.contains(";")) {
            String[] values = value.split(";");
            for (String val : values) {
                String[] vals = val.split("=");
                if (vals.length > 1) {
                    map.put(vals[0], vals[1]);
                }
            }
        } else {
            String[] values = value.split("=");
            map.put(values[0], values[1]);
        }
        return map;
    }

    public static Tuple2<File, String> recogImage(LogController log, byte[] image, boolean saveImg) {
        client.setConnectionTimeoutInMillis(2000);
        client.setSocketTimeoutInMillis(60000);
        File file = null;
        if (saveImg) {
            file = new File(CommonUtil.getHomeImagePath() + IdUtil.fastSimpleUUID() + ".jpg");
            FileUtil.writeBytes(image, file);
        }
        try {
            JSONObject res = client.basicGeneral(image, new HashMap<>());
            JSONArray array = (JSONArray) res.get("words_result");
            if (array.length() > 0) {
                JSONObject data = (JSONObject) array.get(0);
                return Tuple.of(file, (String) data.get("words"));
            }
            return Tuple.of(file, StringUtils.EMPTY);
        } catch (Exception e) {
            log.addLog("小度识别有点累了 %s 休息一会", e.getMessage());
            try {
                Thread.sleep(500L);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
            log.addLog("小度再来一次", e.getMessage());
            return recogImage(log, image, false);
        }
    }

    public static Tuple2<File, String> recogImage(byte[] image, boolean saveImg) {
        client.setConnectionTimeoutInMillis(2000);
        client.setSocketTimeoutInMillis(60000);
        File file = null;
        if (saveImg) {
            file = new File(CommonUtil.getHomeImagePath() + IdUtil.fastSimpleUUID() + ".jpg");
            FileUtil.writeBytes(image, file);
        }
        try {
            JSONObject res = client.basicGeneral(image, new HashMap<>());
            JSONArray array = (JSONArray) res.get("words_result");
            if (null != array && array.length() > 0) {
                JSONObject data = (JSONObject) array.get(0);
                return Tuple.of(file, (String) data.get("words"));
            }
            return Tuple.of(file, StringUtils.EMPTY);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Tuple.of(file, StringUtils.EMPTY);
    }

    public static HttpResponse getImage() {
        final HttpRequest request = HttpUtil.createGet("http://m1mmlmm.apgn3si.buzz/getcode.asp?timestamp=" + RandomUtil.randomInt(1000, 9999));
        return request.execute();
    }

    public static HttpResponse reqWithImage(List<HttpCookie> cookies, String url, String body) {
        final HttpRequest request = HttpUtil.createPost(url);
        request.cookie(cookies);
        request.setMethod(Method.POST);
        request.body(body);
        return request.execute();
    }

    public static void main(String[] args) throws TesseractException {
        HttpResponse response = getImage();
        Tuple2<File, String> tp2 = Tuple.of(null, "");
        while (StringUtils.length(tp2._2) < 4) {
            response = getImage();

            File file = new File(CommonUtil.getHomeImagePath() + IdUtil.fastSimpleUUID() + ".bmp");
            FileUtil.writeBytes(response.bodyBytes(), file);

            ITesseract tesseract = new Tesseract();
            tesseract.setDatapath("/Users/yeweidong/tesseract/tessdata");
            tesseract.setVariable("tessedit_char_whitelist", "0123456789");
            tesseract.setConfigs(Collections.singletonList("digits"));
            String ret = tesseract.doOCR(file);

            //tp2 = recogImage(, true);
            System.out.println("code=" + ret);
        }

        final String url = "http://m1mmlmm.apgn3si.buzz/reg_ok.asp";
        final String body = "you=1&userName=tencent&createTime=1443489891&userPass=123456q&userPasschk=" + tp2._2
            + "&userEmail=%C7%EB%CC%EE%D0%B4%D3%CA%CF%E4%BA%C5%C2%EB%40qq.com&chkCode=8172&regBtn=%CD%AC%D2%E2%D2%D4%C9%CF%D0%AD%D2%E9%2C%CC%E1%BD%BB%D7%A2%B2%E1";
        final HttpResponse response2 = reqWithImage(response.getCookies(), url, body);
        System.out.println(response2.body());
    }

    /**
     * Method for get System Name
     *
     * @return Host name
     */
    public static String getSystemName() {
        try {
            InetAddress inetaddress = InetAddress.getLocalHost();
            return inetaddress.getHostName();
        } catch (Exception E) {
            E.printStackTrace();
            return null;
        }
    }

    public static Long nextId() {
        return snowflake.nextId();
    }

    public static String date2Str(Date date) {
        if (null == date) {
            return null;
        }
        return new SimpleDateFormat(BaseRepository.DATE_FORMAT).format(date);
    }

    public static String trimEnd(String inStr, String suffix) {
        while (inStr.endsWith(suffix)) {
            inStr = inStr.substring(0, inStr.length() - suffix.length());
        }
        return inStr;
    }

    public static String sub(String text, int length) {
        if (StringUtils.length(text) > length) {
            return StringUtils.substring(text, 0, length);
        }
        return StringUtils.rightPad(text, 4, " ");
    }

    public static void sendNotice(String msg, Object... args) {
        Email email = new EmailBuilder()
            .from("TrService", "net52job@163.com")
            .to("developer", "1067521725@qq.com")
            .subject("抢单通知")
            .text(String.format(msg, args))
            .build();
        new Mailer("smtp.163.com", 25, "net52job@163.com", "net520").sendMail(email);
    }

}
